package com.netease.nie.base.http;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;

import com.google.common.util.concurrent.ExecutionList;
import com.google.common.util.concurrent.ForwardingFuture;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

/**
 * An adapter to turn a {@link Future} into a {@link ListenableFuture}.  This
 * will wait on the future to finish, and when it completes, run the
 * listeners.  This implementation will wait on the source future
 * indefinitely, so if the source future never completes, the adapter will
 * never complete either.
 *
 * <p>If the delegate future is interrupted or throws an unexpected unchecked
 * exception, the listeners will not be invoked.
 *
 * <p>Note：add setDaemon(true)</p>
 */
class ListenableFutureAdapter<V> extends ForwardingFuture<V> implements ListenableFuture<V> {

    private static final ThreadFactory threadFactory =
        new ThreadFactoryBuilder()
            .setDaemon(true)
            .setNameFormat("ListenableFutureAdapter-thread-%d")
            .build();

    private static final Executor defaultAdapterExecutor = Executors.newCachedThreadPool(threadFactory);

    private final Executor adapterExecutor;

    // The execution list to hold our listeners.
    private final ExecutionList executionList = new ExecutionList();

    // This allows us to only start up a thread waiting on the delegate future
    // when the first listener is added.
    private final AtomicBoolean hasListeners = new AtomicBoolean(false);

    // The delegate future.
    private final Future<V> delegate;

    ListenableFutureAdapter(Future<V> delegate) {
        this(delegate, defaultAdapterExecutor);
    }

    ListenableFutureAdapter(Future<V> delegate, Executor adapterExecutor) {
        this.delegate = checkNotNull(delegate);
        this.adapterExecutor = checkNotNull(adapterExecutor);
    }

    @Override
    protected Future<V> delegate() {
        return delegate;
    }

    @Override
    public void addListener(Runnable listener, Executor exec) {
        executionList.add(listener, exec);

        // When a listener is first added, we run a task that will wait for
        // the delegate to finish, and when it is done will run the listeners.
        if (hasListeners.compareAndSet(false, true)) {
            executionList.execute();
        }
    }

}
